package com.tsq.intelligence.provider.service.impl;

import com.alibaba.fastjson.JSON;
import com.tsq.intelligence.api.dto.VoiceDTO;
import com.tsq.intelligence.api.vo.VoiceVO;
import com.tsq.intelligence.provider.config.Config;
import com.tsq.intelligence.provider.service.IVoiceService;
import com.tsq.intelligence.provider.utils.Constant;
import com.tsq.intelligence.provider.utils.EncryptUtil;
import com.tsq.intelligence.provider.utils.HttpUtil;
import com.tsq.intelligence.provider.utils.SliceIdGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.security.SignatureException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class VoiceServiceImpl implements IVoiceService {

    @Autowired
    private Config config;

    @Override
    public VoiceVO transferVoiceToTextDelay(VoiceDTO voiceDTO,MultipartFile audio) throws IOException {
        String resultStr = "";
        FileInputStream fis = null;
        VoiceVO voiceVO = null;
        try {
            //MultipartFile 转  File
            File file = inputStreamToFile(audio);

            // 预处理
            String taskId = prepare(voiceDTO,file);

            // 轮询分片上传文件
            fis = uploadSliceCycle(taskId,file,fis);

            // 合并文件
            merge(taskId);

            // 获取最终结果
            resultStr = getResultData(taskId,fis);
            voiceVO = VoiceVO.retData(resultStr);

        } catch (SignatureException e) {
            e.printStackTrace();
            log.info("语音转写异常：" + e.getMessage());
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
            log.info("语音转写异常：" + e1.getMessage());
        } catch (IOException e1) {
            e1.printStackTrace();
            log.info("语音转写异常：" + e1.getMessage());
        }finally {
            fis.close();
        }
        return voiceVO;
    }

    /**
     * MultipartFile  转  File
     *
     * @return
     * @throws SignatureException
     */
    public File inputStreamToFile(MultipartFile audio) throws IOException {
        InputStream ins = null;
        File file = null;
        OutputStream os = null;
        try {
            if(audio.equals("")||audio.getSize()<=0){
                audio = null;
            }else {
                ins = audio.getInputStream();
                file = new File(audio.getOriginalFilename());
            }
            os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            os.close();
            ins.close();
        }
        return file;
    }

    /**
     * 获取每个接口都必须的鉴权参数
     *
     * @return
     * @throws SignatureException
     */
    public Map<String, Object> getBaseAuthParam(String taskId) throws SignatureException {
        Map<String, Object> baseParam = new HashMap<String, Object>();
        String ts = String.valueOf(System.currentTimeMillis() / 1000L);
        baseParam.put("app_id", config.getAppId());
        baseParam.put("ts", ts);
        baseParam.put("signa", EncryptUtil.HmacSHA1Encrypt(EncryptUtil.MD5(config.getAppId()+ ts), config.getVoiceApiSecret()));
        if (taskId != null) {
            baseParam.put("task_id", taskId);
        }
        return baseParam;
    }

    /**
     * 预处理
     *
     * @param file     需要转写的音频
     * @return
     * @throws SignatureException
     */
    public String prepare(VoiceDTO voiceDTO,File file) throws SignatureException {
        Map<String, Object> prepareParam = getBaseAuthParam(null);
        long fileLenth = file.length();
        prepareParam.put("file_len", fileLenth + "");
        prepareParam.put("file_name", file.getName());
        prepareParam.put("slice_num", (fileLenth/Constant.SLICE_SIZE) + (fileLenth % Constant.SLICE_SIZE == 0 ? 0 : 1) + "");
        /********************TODO 可配置参数********************/
        // 转写类型
        prepareParam.put("lfasr_type", voiceDTO.getLfasrType());
        // 开启分词
        prepareParam.put("has_participle", voiceDTO.getHasParticiple());
        // 设置多候选词个数
        prepareParam.put("max_alternatives", voiceDTO.getMaxAlternatives());
        // 发音人个数
        prepareParam.put("speaker_number", voiceDTO.getSpeakerNumber());
        // 说话人分离
        prepareParam.put("has_seperate", voiceDTO.getHasSeperate());
        // 角色类型
        prepareParam.put("role_type", voiceDTO.getRoleType());
        // 是否进行敏感词检出
        prepareParam.put("has_sensitive", voiceDTO.getHasSensitive());
        // 敏感词类型
        prepareParam.put("sensitive_type", voiceDTO.getSensitiveType());
        // 关键词
        prepareParam.put("keywords", voiceDTO.getKeywords());
        // 语种
        prepareParam.put("language", voiceDTO.getLanguage());
        // 垂直领域个性化参数
        prepareParam.put("pd", voiceDTO.getPd());
        /****************************************************/
        // 组装请求头
        Map<String, String> header = new HashMap<String, String>();
        header.put("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
        String response = HttpUtil.doPost(Constant.LFASR_URL+Constant.PREPARE,header,prepareParam);
        if (response == null) {
            throw new RuntimeException("预处理接口请求失败！");
        }
        VoiceVO resultDto = JSON.parseObject(response, VoiceVO.class);
        String taskId = resultDto.getData();
        if (resultDto.getOk() != 0 || taskId == null) {
            throw new RuntimeException("预处理失败！" + response);
        }
        return taskId;
    }

    // 轮询分片上传文件
    public FileInputStream uploadSliceCycle(String taskId,File file,FileInputStream fis) throws IOException, SignatureException {
        int len = 0;
        byte[] slice = new byte[Constant.SLICE_SIZE];
        SliceIdGenerator generator = new SliceIdGenerator();
        fis = new FileInputStream(file);
        while ((len =fis.read(slice)) > 0) {
            // 上传分片
            if (fis.available() == 0) {
                slice = Arrays.copyOfRange(slice, 0, len);
            }
            uploadSlice(taskId, generator.getNextSliceId(), slice,file.getName());
        }
        return fis;
    }

    /**
     * 分片上传
     *
     * @param taskId        任务id
     * @param slice         分片的byte数组
     * @throws SignatureException
     */
    public void uploadSlice(String taskId, String sliceId, byte[] slice,String fileName) throws SignatureException {
        //组装请求体
        Map<String, Object> uploadParam = getBaseAuthParam(taskId);
        uploadParam.put("slice_id", sliceId);
        // 组装请求头
        Map<String, String> header = new HashMap<String, String>();
        header.put("Content-Type", "multipart/form-data");
        String response = HttpUtil.doPost(Constant.LFASR_URL + Constant.UPLOAD,header,uploadParam,slice,fileName);
        if (response == null) {
            throw new RuntimeException("分片上传接口请求失败！");
        }
        if (JSON.parseObject(response).getInteger("ok") == 0) {
            return;
        }
        throw new RuntimeException("分片上传失败！" + response + "|" + taskId);
    }

    /**
     * 文件合并
     *
     * @param taskId        任务id
     * @throws SignatureException
     */
    public void merge(String taskId) throws SignatureException {
        // 组装请求头
        Map<String, String> header = new HashMap<String, String>();
        header.put("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
        String response = HttpUtil.doPost(Constant.LFASR_URL + Constant.MERGE,header,getBaseAuthParam(taskId));
        if (response == null) {
            throw new RuntimeException("文件合并接口请求失败！");
        }
        if (JSON.parseObject(response).getInteger("ok") == 0) {
            return;
        }
        throw new RuntimeException("文件合并失败！" + response);
    }

    /**
     * 获取任务进度
     *
     * @param taskId        任务id
     * @throws SignatureException
     */
    public VoiceVO getProgress(String taskId) throws SignatureException {
        // 组装请求头
        Map<String, String> header = new HashMap<String, String>();
        header.put("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
        String response = HttpUtil.doPost(Constant.LFASR_URL + Constant.GET_PROGRESS,header,getBaseAuthParam(taskId));
        if (response == null) {
            throw new RuntimeException("获取任务进度接口请求失败！");
        }
        return JSON.parseObject(response, VoiceVO.class);
    }

    /**
     * 获取转写结果
     *
     * @param taskId
     * @return
     * @throws SignatureException
     */
    public String getResult(String taskId) throws SignatureException {
        // 组装请求头
        Map<String, String> header = new HashMap<String, String>();
        header.put("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
        String responseStr = HttpUtil.doPost(Constant.LFASR_URL + Constant.GET_RESULT,header,getBaseAuthParam(taskId));
        if (responseStr == null) {
            throw new RuntimeException("获取结果接口请求失败！");
        }
        System.out.println("获取结果responseStr  =" + responseStr);
        return responseStr;
    }

    // 轮询获取任务结果
    public String getResultData(String taskId,FileInputStream fis) throws SignatureException, IOException {
        String resultStr = "";
        while (true) {
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            VoiceVO taskProgress = getProgress(taskId);
            log.info("轮询获取任务结果：" + JSON.toJSONString(taskProgress));
            if (taskProgress.getOk() == 0) {
                if (taskProgress.getErrNo() != 0) {
                    log.info("任务失败：" + JSON.toJSONString(taskProgress));
                }
                String taskStatus = taskProgress.getData();
                if (JSON.parseObject(taskStatus).getInteger("status") == 9) {
                    log.info("任务完成");
                    // 获取结果
                    resultStr = getResult(taskId);
                    fis.close();
                    break;
                }
                log.info("任务处理中：" + taskStatus);
            } else {
                log.info("获取任务进度失败！");
            }
        }
        return resultStr;
    }

}
